/*
 * Copyright (c) 2015 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/shell/shell.h>
#include <version.h>
#include <zephyr/logging/log.h>
#include <stdlib.h>
#include <stdio.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/usb/usb_device.h>
#include <ctype.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/sys/util.h>
#include <zephyr/linker/linker-defs.h>
#include <zephyr/device.h>
#include <zephyr/shell/shell_uart.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/devicetree.h>
#include <../../dolphin_common/include/motor.h>
#include <../../dolphin_common/include/Ymodem.h>
#include <../../dolphin_common/include/relay.h>
#include <../../dolphin_common/include/common.h>
#include <../../dolphin_common/include/servo.h>
#include <../../dolphin_common/include/stepmotor.h>
#include <../../dolphin_common/include/jetson_nano.h>
/*********************************************************************
 * 需要的标志定义
 *********************************************************************/
#define MAIN_ROM   1
#define BACKUP_ROM 2
#define ROM_NORMAL 0
#define ROM_ERROR -1



#define APP_OFFSET_ADDR       	0x00020000  //定义app的基地址，相应得要在app工程文件menuconfig中修改
#define BOOT_SIZE_SIGN_ADDR  	0x000EFFE0  //flash的一个区域划为boot程序大小标记
#define APP_SIZE_SIGN_ADDR  	0x000EFFF0  //flash的一个区域划为app程序大小标记

#define JUMP_APP_STACK_SIZE 1024
#define JUMP_APP_PRIORITY   3

/*********************************************************************
 * 函数声明
 *********************************************************************/
LOG_MODULE_REGISTER(boot);
int32_t SerialDownload(uint32_t addrDst);
void jump_to_addr(uint32_t addr);
void download_function(void *p1, void *p2, void *p3);
void boot_shell_uninit_cb(const struct shell *shell, int res);
void shell_reinit_trigger(void);
void shell_uninit_download_cb(const struct shell *shell, int res);
void wait_update_timer_handler(struct k_timer *timer);
void int2str(int n, char *str);
int str2int(const char *str);
int watchdog_init(void);
static int app_rom_check(int type);
static void app_rom_rollback(void);


/*********************************************************************
 * 全局变量及外部变量声明
 *********************************************************************/
k_thread_stack_t  jump_app_stack_area[JUMP_APP_STACK_SIZE];
struct k_thread jump_app_thread;
k_tid_t jump_app_tid;
K_SEM_DEFINE(jump_to_app_sem, 0, 1);
K_TIMER_DEFINE(wait_update_timer, wait_update_timer_handler, NULL);
const struct device *const shell_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
uint32_t FLASH_BASE_ADDR;
/*******************************************************************************
  * @函数名称	wait_update_timer_handler
  * @函数说明   5s定时决定是否跳转app
  * @输入参数   定时器接口
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
void wait_update_timer_handler(struct k_timer *timer)
{
	static int cnt = 5;
	cnt--;
	if(0 == cnt){
		 k_sem_give(&jump_to_app_sem);
		 k_timer_stop(&wait_update_timer);
		 shell_reinit_trigger();
	}
	else{
		printk("\r\n%d seconds left to jump app , press Esc to stop.",cnt);
	}
}

void jump_app_function(void *p1, void *p2, void *p3)
{
	if(k_sem_take(&jump_to_app_sem, K_FOREVER) == 0) {
		k_timer_stop(&wait_update_timer);
		if(app_rom_check(MAIN_ROM) == ROM_NORMAL){
			jump_to_addr(FLASH_BASE_ADDR + APP_OFFSET_ADDR);
		}
		else{
			printk("\r\napp main rom check error,try rollback backup rom\r\n");
			LOG_ERR("\r\napp main rom check error,try rollback backup rom\r\n");

			if(app_rom_check(BACKUP_ROM) == ROM_NORMAL){
				app_rom_rollback();
				jump_to_addr(FLASH_BASE_ADDR + APP_OFFSET_ADDR);
			}
			else{
				printk("\r\napp all rom check error,please download new\r\n");
				LOG_ERR("\r\napp all rom check error,,please download new\r\n");
			}
		}
	}
}


/*******************************************************************************
  * @函数名称	app_rom_check
  * @函数说明   app rom 包检查
  * @输入参数   MAIN ROM 还是 BACKUP ROM
  * @输出参数   无
  * @返回参数   ROM包是否完整
*******************************************************************************/
static int app_rom_check(int type)
{
	return ROM_NORMAL;
}


/*******************************************************************************
  * @函数名称	app_rom_rollback
  * @函数说明   app rom 由backup区回退
  * @输入参数   无
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
static void app_rom_rollback(void)
{

}


void main(void)
{
	printk("\r\nEnter BootLoader ...\r\n");
	LOG_INF("Enter BootLoader ...");
	//确定当前flash基地址
	if(strcmp(CONFIG_BOARD,"stm32f4_disco")==0){
		FLASH_BASE_ADDR = 0x08000000;
		LOG_INF("BOARD is stm32f4_disco");
	}
	else if(strcmp(CONFIG_BOARD, "esp32")){
		FLASH_BASE_ADDR = 0x40000000;
		LOG_INF("BOARD is esp32");
	}
	
	//看门狗初始化
	watchdog_init();

	//5s计时到了跳转app
	shell_uninit(shell_backend_uart_get_ptr(), boot_shell_uninit_cb); //先关闭shell等待5s决定是否跳app
	k_timer_start(&wait_update_timer, K_SECONDS(1), K_SECONDS(1));
	k_sem_take(&jump_to_app_sem, K_NO_WAIT);
	jump_app_tid = k_thread_create(&jump_app_thread,jump_app_stack_area, JUMP_APP_STACK_SIZE,
                                 jump_app_function, NULL, NULL, NULL,
                                  JUMP_APP_PRIORITY, 0, K_NO_WAIT);
}
